import Overlay from 'ol/Overlay'
import { toLonLat } from 'ol/proj'
import { feature2Geojson } from '../utils/map'
import { point as TurfPoint, lineString } from '@turf/helpers'
import polygonToLine from '@turf/polygon-to-line'
import pointToLineDistance from '@turf/point-to-line-distance'
import nearestPointOnLine from '@turf/nearest-point-on-line'
import distance from '@turf/distance'

class SnapTool {
  /**
   * 初始化构造函数
   * @param {ol.Map} map - map对象
   * @param {ol.source.Vector} vectorSource - vectorSource，捕捉图层
   * @param {Object} options -参数
   * @param {number} [options.pixelTolerance = 10] - 容差值
   */
  constructor (map, vectorSource, options = {}) {
    this._overlay = null
    this._map = map
    this._vectorSource = vectorSource
    this._options = Object.assign({
      pixelTolerance: 10,
      active: true
    }, options)
    this._active = this._options.active
    this._init()
  }

  _init () {
    const marker = document.createElement('div')
    marker.classList.add('snap-marker')
    this._overlay = new Overlay({
      element: marker,
      positioning: 'center-center',
      stopEvent: false,
      offset: [0, 0]
    })
    this._map.addOverlay(this._overlay)
    this._setPosition(null)
  }

  _setPosition (pos) {
    this._overlay.setPosition(pos)
  }

  /**
   * 获取捕捉点
   * @param {Array} coords - 输入坐标
   * @return {Array} - 输出坐标，如果不满足条件，则返回 []
   */
  getSnapPoint (coords) {
    if (!this._active) return []
    let result = []
    let features = this._vectorSource.getFeatures()
    this._setPosition(coords)
    const res = this._map.getView().getResolution()
    const dis = this.getPixelTolerance() * res
    coords = TurfPoint(toLonLat(coords))
    for (let i = 0; i < features.length; i++) {
      const feature = features[i]
      const json = feature2Geojson(feature)
      const geomType = json.geometry.type
      if (geomType === 'Point') {
        result = this._getPointSnapPoint(coords, json, dis)
      } else if (geomType.indexOf('LineString') !== -1) {
        result = this._getLineSnapPoint(coords, json, dis)
      } else if (geomType.indexOf('Polygon') !== -1) {
        result = this._getPolygonSnapPoint(coords, json, dis)
      }
      if (result.length > 0) break
    }
    if (result.length > 0) this._setPosition(result)
    return result
  }

  // 捕捉点
  _getPolygonSnapPoint (coords, polygon, dis) {
    const line = polygonToLine(polygon)
    let res = []
    if (line.features) {
      const fetures = line.features
      for (let i = 0; i < fetures.length; i++) {
        res = this._getLineSnapPoint(coords, fetures[i], dis)
        if (res.length > 0) break
      }
    } else {
      res = this._getLineSnapPoint(coords, line, dis)
    }
    return res
  }

  // 捕捉线
  _getLineSnapPoint (coords, line, dis) {
    let result = []
    if (line.geometry.type === 'MultiLineString') {
      const coordinates = line.geometry.coordinates
      for (let i = 0; i < coordinates.length; i++) {
        const _line = lineString(coordinates[i])
        const dist = pointToLineDistance(coords, _line)
        if (dist * 1000 < dis) {
          const pointLine = nearestPointOnLine(_line, coords, { units: 'kilometers' })
          result = pointLine.geometry.coordinates
          break
        }
      }
    } else {
      const dist = pointToLineDistance(coords, line)
      if (dist * 1000 < dis) {
        const pointLine = nearestPointOnLine(line, coords, { units: 'kilometers' })
        result = pointLine.geometry.coordinates
      }
    }
    return result
  }

  // 捕捉点
  _getPointSnapPoint (coords, point, dis) {
    let result = []
    const dist = distance(coords, point)
    if (dist * 1000 < dis) {
      result = point.geometry.coordinates
    }
    return result
  }

  /**
   * 设置是否可用
   * @param {boolean} [active = true] - 是否激活
   */
  setActive (active = true) {
    this._active = active
  }

  /**
   * 获取捕捉容差
   * @return {number} - 获取捕捉容差
   */
  getPixelTolerance () {
    return this._options.pixelTolerance
  }

  /**
   * 设置捕捉容差
   * @param {number} pixelTolerance - 捕捉容差值
   */
  setPixelTolerance (pixelTolerance) {
    this._options.pixelTolerance = pixelTolerance
  }
}

export default SnapTool
